﻿namespace UCSD_BioLit
{
    #region Namespace

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.Office.Core;
    using UCSD_BioLit.Properties;
    using MSWord = Microsoft.Office.Interop.Word;
    using System.Diagnostics;

    #endregion

    /// <summary>
    /// Helper class for adding Ontology options to the selected text on the Word context menu.
    /// </summary>
    public class ContextMenuHelper
    {
        /// <summary>
        /// The Word context menus to which Ontology controls need to be added.
        /// </summary>
        public static readonly string[] OntologyContextMenus = new string[] { "Text", "Spelling", "Grammar", "Grammar (2)" };

        /// <summary>
        /// Tag Id of the controls added to the Context Menu.
        /// </summary>
        public static readonly string ContextMenuTag = "8443E043-7794-4cbc-BCF5-4DD282FE0E27";

        /// <summary>
        /// Class name for exception title.
        /// </summary>
        private const string ExceptionString = "ContextMenuHelper";

        /// <summary>
        /// Event handler for the WindowBeforeRightClick event.
        /// </summary>
        public MSWord.ApplicationEvents4_WindowBeforeRightClickEventHandler WindowBeforeRightClick;

        public MSWord.ApplicationEvents4_DocumentBeforeCloseEventHandler DocumentBeforeClose;

        public MSWord.ApplicationEvents4_DocumentBeforeSaveEventHandler DocumentBeforeSave;

        /// <summary>
        /// Instance of the class ContextMenuHelper.
        /// </summary>
        private static ContextMenuHelper _contextMenuHelper;

        /// <summary>
        /// Term selected by the user.
        /// </summary>
        private string _defaultMarkupTerm = String.Empty;

        /// <summary>
        /// Range of the term selected by the user.
        /// </summary>
        private MSWord.Range _defaultMarkupTermRange;
        
        /// <summary>
        /// Instance of KeyboardEventHook class used to hook keyboard events.
        /// </summary>
        private KeyboardEventHook _keyboardHook;

        /// <summary>
        /// Prevents a default instance of the ContextMenuHelper class from being created.
        /// </summary>
        private ContextMenuHelper()
        {
        }

        /// <summary>
        /// Event handler for RecognizeTermsEvent.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">TermRecognitionEventArgs for the selection.</param>
        public delegate void TermRecognitionEventHandler(object sender, TermRecognitionEventArgs e);

        /// <summary>
        /// Event raised to create the Context menu controls.
        /// </summary>
        public event EventHandler CreateMenuControlsEvent;

        /// <summary>
        /// Event raised to recognize terms.
        /// </summary>
        public event TermRecognitionEventHandler RecognizeTermsEvent;

        /// <summary>
        /// Gets an Instance of the class ContextMenuHelper.
        /// </summary>
        /// <returns>The ContextMenuHelper instance.</returns>
        public static ContextMenuHelper Instance()
        {
            if (_contextMenuHelper == null)
            {
                _contextMenuHelper = new ContextMenuHelper();
                _contextMenuHelper._keyboardHook = new KeyboardEventHook();

                WireEvents();
            }

            return _contextMenuHelper;
        }

        /// <summary>
        /// Method to clear the Ontology options from Word context menus.
        /// </summary>
        public void ClearBiolitMenus()
        {
            foreach (string contextMenuName in ContextMenuHelper.OntologyContextMenus)
            {
                try
                {
                    CommandBar contextMenu = Globals.ThisAddIn.Application.CommandBars[contextMenuName.ToString()];

                    if (contextMenu != null)
                    {
                        CommandBarPopup popupControl = (CommandBarPopup)contextMenu.FindControl(
                            MsoControlType.msoControlPopup, Type.Missing, ContextMenuHelper.ContextMenuTag, true, true);

                        if (popupControl != null)
                        {
                            popupControl.Delete(true);
                        }
                    }

                    ((MSWord.Template)Globals.ThisAddIn.Application.ActiveDocument.get_AttachedTemplate()).Saved = true;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionString);
                }
            }
        }

        /// <summary>
        /// Method to add the "Ontology - Mark-up Selection" option to the Word context menus.
        /// </summary>
        public void AddBiolitMenus()
        {
            foreach (string contextMenuName in ContextMenuHelper.OntologyContextMenus)
            {
                CommandBar contextMenu = Globals.ThisAddIn.Application.CommandBars[contextMenuName.ToString()];

                if (contextMenu != null)
                {
                    CommandBarPopup popupControl = (CommandBarPopup)contextMenu.FindControl(
                        MsoControlType.msoControlPopup, Type.Missing, ContextMenuHelper.ContextMenuTag, true, true);

                    if (popupControl == null)
                    {
                        try
                        {
                            popupControl = (CommandBarPopup)contextMenu.Controls.Add(
                                    MsoControlType.msoControlPopup, System.Type.Missing, System.Type.Missing, System.Type.Missing, true);

                            if (popupControl != null)
                            {
                                popupControl.Caption = Resources.OntologyContextMenuText;
                                popupControl.Tag = ContextMenuHelper.ContextMenuTag;
                            }
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message, ExceptionString);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Wires the Word application events.
        /// </summary>
        private static void WireEvents()
        {
            Globals.ThisAddIn.Application.WindowActivate += _contextMenuHelper.WindowActivate;
            Globals.ThisAddIn.Application.WindowDeactivate += _contextMenuHelper.WindowDeactivate;

            _contextMenuHelper.WindowBeforeRightClick =
            new MSWord.ApplicationEvents4_WindowBeforeRightClickEventHandler(_contextMenuHelper.WordAppWindowBeforeRightClick);

            _contextMenuHelper.DocumentBeforeClose =
                new MSWord.ApplicationEvents4_DocumentBeforeCloseEventHandler(_contextMenuHelper.WordAppDocumentBeforeClose);

            _contextMenuHelper.DocumentBeforeSave =
            new MSWord.ApplicationEvents4_DocumentBeforeSaveEventHandler(_contextMenuHelper.WordAppDocumentBeforeSave);

            // Keyboard click event.
            _contextMenuHelper._keyboardHook.KeyboardRightClickEvent += _contextMenuHelper.OnKeyboardRightClickEvent;
        }

        /// <summary>
        /// Invoked when WindowBeforeRightClick event occurs.
        /// </summary>
        /// <param name="selection">The current selection.</param>
        /// <param name="cancel">
        /// False when the event occurs. 
        /// If set to True, the default right-click action does not occur when the procedure is finished.
        /// </param>
        private void WordAppWindowBeforeRightClick(MSWord.Selection selection, ref bool cancel)
        {
            this.PopulateContextMenuOptions(selection);
        }

        private void WordAppDocumentBeforeClose(MSWord.Document document, ref bool cancel)
        {
            this.ClearBiolitMenus();
        }

        private void WordAppDocumentBeforeSave(MSWord.Document document, ref bool saveAsUI, ref bool cancel)
        {
            this.ClearBiolitMenus();
        }

        /// <summary>
        /// Sets the current selection text, range and populates the context menus.
        /// </summary>
        /// <param name="selection">The current selection.</param>
        private void PopulateContextMenuOptions(MSWord.Selection selection)
        {
            MSWord.Range selectionRange = null;
            int cursorPosition;
            string selectedText = String.Empty;
            bool isSelection;

            if (selection.Paragraphs.Count > 0 && !String.IsNullOrEmpty(selection.Paragraphs.First.Range.Text))
            {
                // Check if the user has selected some text. If yes, only terms withing this text are added to the list.
                if (selection.Range != null && !String.IsNullOrEmpty(selection.Range.Text))
                {
                    selectedText = selection.Range.Text;
                    selectionRange = selection.Range;
                    cursorPosition = selection.Range.Start;
                    isSelection = true;

                    _contextMenuHelper._defaultMarkupTerm = selectedText;
                    _contextMenuHelper._defaultMarkupTermRange = selectionRange;
                }
                else
                {
                    selectedText = selection.Paragraphs.First.Range.Text;
                    selectionRange = selection.Paragraphs.First.Range;
                    cursorPosition = selection.Range.Start;
                    isSelection = false;

                    if (selection.Words.Count > 0 && !String.IsNullOrEmpty(selection.Words.First.Text))
                    {
                        _contextMenuHelper._defaultMarkupTerm = selection.Words.First.Text;
                        _contextMenuHelper._defaultMarkupTermRange = selection.Words.First;
                    }
                }

                _contextMenuHelper.RecognizeTerms(selectedText, selectionRange, cursorPosition, isSelection);
            }
        }

        /// <summary>
        /// Method to recognize and add the terms to the list. 
        /// If the current selection is not a recognized Ontology, it is added as the default term.
        /// </summary>
        /// <param name="selectedText">The selected text.</param>
        /// <param name="selectionRange">Range of the current selection.</param>
        /// <param name="cursorPosition">The current cursor position.</param>
        /// <param name="isSelection">Indicates whether the user selected a specific range of text.</param>
        private void RecognizeTerms(string selectedText, MSWord.Range selectionRange, int cursorPosition, bool isSelection)
        {
            // Clear the existing Ontology options from the context menu.
            _contextMenuHelper.ClearBiolitMenus();

            TermRecognitionEventArgs termRecognizeArgs = new TermRecognitionEventArgs
            {
                SelectedText = selectedText,
                SelectionRange = selectionRange,
                CursorPosition = cursorPosition,
                IsSelection = isSelection
            };

            OntologyTermBase ontologyTerm = OntologyTerm.Instance();
            OntologyTermBase ncbiTerm = NCBITerm.Instance();
            OntologyTermBase pdbTerm = PDBTerm.Instance();
            OntologyTermBase uniprotTerm = UniprotTerm.Instance();

            _contextMenuHelper.RaiseRecognizeTermsEvent(termRecognizeArgs);

            // Check if the current term is a recognized Ontology. If not, assign it as the default term.
            if (!String.IsNullOrEmpty(_contextMenuHelper._defaultMarkupTerm.Trim()) &&
                    !ontologyTerm.TermCollection.Values.Any(delegate(RecognizedTermProperties props)
                    {
                        return props.RecognizedTerm == _contextMenuHelper._defaultMarkupTerm.Trim();
                    }))
            {
                ((OntologyTerm)ontologyTerm).DefaultTermId = Guid.NewGuid().ToString();
                ((OntologyTerm)ontologyTerm).DefaultTerm = _contextMenuHelper._defaultMarkupTerm;
                ((OntologyTerm)ontologyTerm).DefaultTermRange = _contextMenuHelper._defaultMarkupTermRange;
            }

            if (ontologyTerm.TermCollection.Count > 0 || ncbiTerm.TermCollection.Count > 0 ||
                pdbTerm.TermCollection.Count > 0 || uniprotTerm.TermCollection.Count > 0 ||
                !String.IsNullOrEmpty(_contextMenuHelper._defaultMarkupTerm.Trim()))
            {
                _contextMenuHelper.AddBiolitMenus();
                _contextMenuHelper.RaiseCreateMenuControlsEvent();
            }
        }

        /// <summary>
        /// Raise the CreateMenuControlsEvent to add menu controls for terms.
        /// </summary>
        private void RaiseCreateMenuControlsEvent()
        {
            if (_contextMenuHelper.CreateMenuControlsEvent != null)
            {
                _contextMenuHelper.CreateMenuControlsEvent(null, null);
            }
        }

        /// <summary>
        /// Raise the RecognizeTermsEvent to trigger term recognition.
        /// </summary>
        /// <param name="e">TermRecognitionEventArgs for the event.</param>
        private void RaiseRecognizeTermsEvent(TermRecognitionEventArgs e)
        {
            if (_contextMenuHelper.RecognizeTermsEvent != null)
            {
                _contextMenuHelper.RecognizeTermsEvent(null, e);
            }
        }

        /// <summary>
        /// Method to handle keyboard event that triggers right mouse click functionality.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event information.</param>
        private void OnKeyboardRightClickEvent(object sender, EventArgs e)
        {
            this.PopulateContextMenuOptions(Globals.ThisAddIn.Application.ActiveDocument.ActiveWindow.Selection);
        }

        /// <summary>
        /// Invoked when the WindowActivate is fired.
        /// WindowActivate event occurs when any document window is activated.
        /// </summary>
        /// <param name="document">The document displayed in the activated window.</param>
        /// <param name="window">The window that's being activated.</param>
        private void WindowActivate(MSWord.Document document, MSWord.Window window)
        {
            _contextMenuHelper._keyboardHook.SetKeyBoardHook();
        }

        /// <summary>
        /// Invoked when the WindowDeactivate is fired.
        /// WindowDeactivate event occurs when any document window is deactivated.
        /// </summary>
        /// <param name="document">The document displayed in the deactivated window.</param>
        /// <param name="window">The deactivated window.</param>
        private void WindowDeactivate(MSWord.Document document, MSWord.Window window)
        {
            _contextMenuHelper._keyboardHook.ClearKeyBoardHook();
        }
    }
}
